iT邦幫忙

2021 iThome 鐵人賽

DAY 19
0

恩,標題不知道怎麼下成中文

在之前的範例裡,示範了如何用coroutine做一次性的網路請求,並交結果post給liveData,讓ui可以觀察(one shot request)


今天,我會聊聊如何將flow用在相同的架構裡面,對observer的資料如何處理,用flow是想解決什麼問題,以及code範例,flow的內容會分成兩篇講,今天用flow和liveData合作,去更新ui

以前的做法

那以前做不到嗎?為什麼要引入flow呢?

其實以前也是能做到的,方法是透過liveData每一層,但和flow相比
liveData要改變內容比較不方便,而且必須是在main thread,最根本的原因就是liveData本來就不是為了當數據流而設計的

同時,這樣的code在clean architecture裡面,並不合適,因為liveData是只有android才有的


貼個圖,因為我也沒有用liveData寫過,所以我去問了別人

順便補個連結,如果有在維護舊專案的人,全用liveData的可以參考這篇LiveData beyond the ViewModel

推薦使用flow

而flow的優勢就是

  1. 可以輕易的切換thread
  2. 減少樣板code
  3. structure consurrency

flow with liveData
透過替換repo和data source的live,實現更有彈性也更易讀的架構

看看code吧

比較

如果不改變資料的話,其實沒差多少

//viewModel

val liveDataResult = repo.postLiveData

val flowResult = repo.postFlow.asLiveData()

添加初始值,覺得讀起來更好懂

val liveDataResult = liveData {
    emit(0)
    emitSource(repo.postLiveData)
}

val flowResult =
    repo.postFlow
        .onStart { emit(0) }
        .asLiveData()

而最重要的,我們講過liveData對資料變更比較不友善,看看code吧

//咦,為甚麼不用liveData的map呢? 因為map跑在main thread
val liveDataResult = 
    repo.postLiveData.switchMap {
        liveData {
            emit( calculateSomething(it) )//繁重的計算需要切換thread, 可以用coroutine的liveData builder
        }
    }

val flowResult =
        repo.postFlow
            .map { calculateSomething(it) }
            .asLiveData()

其實liveData也能處理,就是得寫更多樣版code,而flow寫起來更加直覺,對吧

先別看到這裡就著急說,ㄟ這flow最後還不是得轉成liveData,那我學個毛線?我原本在猶豫要不要講這篇flow with liveData,畢竟之後還會講怎麼完全不用到liveData

但在實際開發上java並沒有coroutine,有些專案是kotlin混java的,甚至是在維護舊專案,要加入新需求,要是只有全flow跟全liveData,引入新技術時會做很多不必要的重工(重寫沒問題的code,測試),所以我才決定留著這一篇

實際應用

非常的簡單,model層我先寫的跟之前的範例一樣用retrofit,在實際開發上,對這種observer的資料,大多會用socket或推播,但也有long polling(聽說比較大多被socket取代),但這篇的簡單範例先用最笨的兩秒打一次

//connect interface
@GET("posts/{num}")
suspend fun getPostFlow(
    @Path ("num") num: Int
): Post
    
//repo
//每兩秒fetch一個新資料
var count = 1
val postFlow: Flow<Post> = flow {
    while (true){
        val result = service.getPostFlow(count)
        emit(result)
        count++
        delay(2000L)
    }
}

//viewModel
val changeId = repo.postFlow.map {
    it.id = -1
    it
}.asLiveData()

//fragment
viewModel.changeId.observe(viewLifecycleOwner){
    binding.apiResultText.text = it.toString()
}

因為太簡單了,實在懶得分開寫

LiveData:还没普及就让我去世?我去你的 Kotlin 协程

liveData with coroutine and flow-1
liveData with coroutine and flow-2
liveData with coroutine and flow-3


上一篇
day18 kotlin - flow基本操作
下一篇
day20 在ui蒐集flow,能取代liveData嗎?
系列文
解鎖kotlin coroutine的各種姿勢-新手篇30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言